package com.atom.module.document.utils;

import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileUtil {
    /**
     * Obtains the actual file path based on the URI (compatible with multiple models).
     *
     * @param context context
     * @param uri     uri
     * @return Return Value
     */
    public static String getFilePathByUri(Context context, Uri uri) {
        if ("content".equalsIgnoreCase(uri.getScheme())) {

            int sdkVersion = Build.VERSION.SDK_INT;
            if (sdkVersion >= 19) { // api >= 19
                return getRealPathFromUriAboveApi19(context, uri);
            } else { // api < 19
                return getRealPathFromUriBelowAPI19(context, uri);
            }
        } else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }
        return null;
    }

    /**
     * Obtains the absolute path of an image based on the URI.
     *
     * @param context Context object
     * @param uri     Image URI
     * @return If the image corresponding to the URI exists, the absolute path of the image is returned.
     * Otherwise, null is returned.
     */
    private static String getRealPathFromUriAboveApi19(Context context, Uri uri) {
        String filePath = null;
        if (DocumentsContract.isDocumentUri(context, uri)) {
            // 如果是document类型的 uri, 则通过document id来进行处理
            String documentId = DocumentsContract.getDocumentId(uri);
            if (isMediaDocument(uri)) { // MediaProvider
                // 使用':'分割
                String type = documentId.split(":")[0];
                String id = documentId.split(":")[1];

                String selection = MediaStore.Images.Media._ID + "=?";
                String[] selectionArgs = {id};

                //
                Uri contentUri = null;
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                }

                filePath = getDataColumn(context, contentUri, selection, selectionArgs);
            } else if (isDownloadsDocument(uri)) { // DownloadsProvider
                Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
                    Long.valueOf(documentId));
                filePath = getDataColumn(context, contentUri, null, null);
            } else if (isExternalStorageDocument(uri)) {
                // ExternalStorageProvider
                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                if ("primary".equalsIgnoreCase(type)) {
                    filePath = Environment.getExternalStorageDirectory() + "/" + split[1];
                }
            } else {
            }
        } else if ("content".equalsIgnoreCase(uri.getScheme())) {
            // URI of the content type
            filePath = getDataColumn(context, uri, null, null);
        } else if ("file".equals(uri.getScheme())) {
            // If the URI of the file type is used, obtain the path of the image.
            filePath = uri.getPath();
        }
        return filePath;
    }

    /**
     * Obtain the absolute path of an image based on the URI.
     *
     * @param context Context object
     * @param uri     Image URI
     * @return If the image corresponding to the URI exists, the absolute path of the image is returned. Otherwise, null is returned.
     */
    private static String getRealPathFromUriBelowAPI19(Context context, Uri uri) {
        return getDataColumn(context, uri, null, null);
    }

    /**
     * Obtain the _data column in the database table, that is, the file path corresponding to the URI is returned.
     *
     * @return return path
     */
    private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
        String path = null;

        String[] projection = new String[]{MediaStore.Images.Media.DATA};
        Cursor cursor = null;
        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
            if (cursor != null && cursor.moveToFirst()) {
                int columnIndex = cursor.getColumnIndexOrThrow(projection[0]);
                path = cursor.getString(columnIndex);
            }
        } catch (Exception e) {
            if (cursor != null) {
                cursor.close();
            }
        }
        return path;
    }

    /**
     * @param uri the Uri to check
     * @return Whether the Uri authority is MediaProvider
     */
    private static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    private static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri the Uri to check
     * @return Whether the Uri authority is DownloadsProvider
     */
    private static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * 复制文件或文件夹
     *
     * @param srcPath 源文件或源文件夹的路径
     * @param destDir 目标文件所在的目录
     * @return
     */
    public static boolean copyGeneralFile(String srcPath, String destDir) {
        boolean flag = false;
        File file = new File(srcPath);
        if (!file.exists()) { // 源文件或源文件夹不存在
            return false;
        }

        if (file.isFile()) {    // 文件复制
            flag = copyFile(srcPath, destDir);
        } else if (file.isDirectory()) { // 文件夹复制
            flag = copyDirectory(srcPath, destDir);
        }

        return flag;
    }

    /**
     * 默认的复制文件方法，默认会覆盖目标文件夹下的同名文件
     *
     * @param srcPath 源文件绝对路径
     * @param destDir 目标文件所在目录
     * @return boolean
     */
    public static boolean copyFile(String srcPath, String destDir) {
        return copyFile(srcPath, destDir, true/*overwriteExistFile*/); // 默认覆盖同名文件
    }

    /**
     * 默认的复制文件夹方法，默认会覆盖目标文件夹下的同名文件夹
     *
     * @param srcPath 源文件夹路径
     * @param destDir 目标文件夹所在目录
     * @return boolean
     */
    public static boolean copyDirectory(String srcPath, String destDir) {
        return copyDirectory(srcPath, destDir, true/*overwriteExistDir*/);
    }

    /**
     * 复制文件到目标目录
     *
     * @param srcPath            源文件绝对路径
     * @param destDir            目标文件所在目录
     * @param overwriteExistFile 是否覆盖目标目录下的同名文件
     * @return boolean
     */
    public static boolean copyFile(String srcPath, String destDir, boolean overwriteExistFile) {
        boolean flag = false;

        File srcFile = new File(srcPath);
        if (!srcFile.exists() || !srcFile.isFile()) { // 源文件不存在
            return false;
        }

        //获取待复制文件的文件名
        String fileName = srcFile.getName();
        String destPath = destDir + File.separator + fileName;
        File destFile = new File(destPath);
        if (destFile.getAbsolutePath().equals(srcFile.getAbsolutePath())) { // 源文件路径和目标文件路径重复
            return false;
        }
        if (destFile.exists() && !overwriteExistFile) {    // 目标目录下已有同名文件且不允许覆盖
            return false;
        }

        File destFileDir = new File(destDir);
        if (!destFileDir.exists() && !destFileDir.mkdirs()) { // 目录不存在并且创建目录失败直接返回
            return false;
        }

        try {
            FileInputStream fis = new FileInputStream(srcPath);
            FileOutputStream fos = new FileOutputStream(destFile);
            byte[] buf = new byte[1024];
            int c;
            while ((c = fis.read(buf)) != -1) {
                fos.write(buf, 0, c);
            }
            fos.flush();
            fis.close();
            fos.close();

            flag = true;
        } catch (IOException e) {
            e.printStackTrace();
        }

        return flag;
    }

    /**
     * @param srcPath 源文件夹路径
     * @param destDir 目标文件夹所在目录
     * @return
     */
    public static boolean copyDirectory(String srcPath, String destDir, boolean overwriteExistDir) {
        if (destDir.contains(srcPath))
            return false;
        boolean flag = false;

        File srcFile = new File(srcPath);
        if (!srcFile.exists() || !srcFile.isDirectory()) { // 源文件夹不存在
            return false;
        }

        //获得待复制的文件夹的名字，比如待复制的文件夹为"E:\\dir\\"则获取的名字为"dir"
        String dirName = srcFile.getName();

        //目标文件夹的完整路径
        String destDirPath = destDir + File.separator + dirName + File.separator;
        File destDirFile = new File(destDirPath);
        if (destDirFile.getAbsolutePath().equals(srcFile.getAbsolutePath())) {
            return false;
        }
        if (destDirFile.exists() && destDirFile.isDirectory() && !overwriteExistDir) {    // 目标位置有一个同名文件夹且不允许覆盖同名文件夹，则直接返回false
            return false;
        }

        if (!destDirFile.exists() && !destDirFile.mkdirs()) {  // 如果目标目录不存在并且创建目录失败
            return false;
        }

        File[] fileList = srcFile.listFiles();    //获取源文件夹下的子文件和子文件夹
        if (fileList == null || fileList.length == 0) {    // 如果源文件夹为空目录则直接设置flag为true，这一步非常隐蔽，debug了很久
            flag = true;
        } else {
            for (File temp : fileList) {
                if (temp.isFile()) {    // 文件
                    flag = copyFile(temp.getAbsolutePath(), destDirPath, overwriteExistDir);     // 递归复制时也继承覆盖属性
                } else if (temp.isDirectory()) {    // 文件夹
                    flag = copyDirectory(temp.getAbsolutePath(), destDirPath, overwriteExistDir);   // 递归复制时也继承覆盖属性
                }

                if (!flag) {
                    break;
                }
            }
        }

        return flag;
    }

    /**
     * 删除文件或文件夹
     *
     * @param path 待删除的文件的绝对路径
     * @return boolean
     */
    public static boolean deleteFile(String path) {
        File file = new File(path);
        if (!file.exists()) { // 文件不存在直接返回
            return false;
        }
        return file.delete();
    }


    /**
     * 由上面方法延伸出剪切方法：复制+删除
     *
     * @param destDir 同上
     */
    public static boolean cutGeneralFile(String srcPath, String destDir) {
        // 复制和删除都成功
        return copyGeneralFile(srcPath, destDir) && deleteFile(srcPath);
    }

    public static void main(String[] args) {
        copyGeneralFile("E://Assemble.txt", "E://New.txt"); // 复制文件
        copyGeneralFile("E://hello", "E://world"); // 复制文件夹
        cutGeneralFile("E://Difficult.java", "E://Cow//"); // 剪切文件
    }


    public interface EachFileCallback {
        void invoke(File file);
    }

    public static void eachFile(File self, EachFileCallback callback) {
        if (self.isFile()) return;
        final File[] files = self.listFiles();
        if (files == null || files.length == 0) return;
        for (File file : files) {
            if (!file.exists()) continue;
            if (file.isFile()) {
                callback.invoke(file);
            } else if (file.isDirectory()) {
                eachFile(file, callback);
            }
        }
    }
}
